home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-09-02 | 20.7 KB | 440 lines | [TEXT/CCL2] |
- ;;; RCS.LISP (Simple Revision Control System)
- ;;; Version 1.1, Jan. '92
- ;;; Functions for managing the editing of project code by multiple people.
- ;;; Hacked by David Neves - neves@ils.nwu.edu
- ;;;
- ;;; Changes:
- ;;; neves (1/21) Make a variable to hold folder of server volume on server machine
- ;;; neves (1/7/92) Server now has a separate working directory.
- ;;; neves (12/23) Updated to MACL 2.0
- ;;; neves (1/23/91)If a copy to the server is cancelled the write date on the server won't be changed.
- ;;;
- ;;; Documentation:
- ;;; On any large project there is a danger of 2 people editing the same file at the same time.
- ;;; Most likely one person's changes will be lost. This software allows someone to "lock" a
- ;;; file so that no one else can edit it. When the user is finished editing the file they
- ;;; can "unlock" the file so that others can edit it.
- ;;;
- ;;; User choices from the "lockfile" menu:
- ;;; - Lock a file. This brings up a dialog so that the user can choose a file to lock. If
- ;;; the file is already locked then the user gets an error message. Locking a file
- ;;; copies the file from the server to the local hard disk. Then the name of the locked
- ;;; file is stored in a special file ("locked-file-list") on the server.
- ;;; - Unlock a file and copy to server. This brings up a dialog with all your locked files.
- ;;; Select 1 or more files (with shift-click) to unlock. The files are copied back to the
- ;;; server and their names are deleted from "locked-file-list".
- ;;; - Unlock a file, but don't copy to server. This is like the choice above but the files
- ;;; are not copied to the server. Useful when the user changes his/her mind about making
- ;;; the changes permanent.
- ;;; - Copy a newly created file to the server. The user has just created a file on his/her
- ;;; hard disk. To move it to the server choose this.
- ;;; - Copy logged files to local disk. This is a quick hack that allows the user to copy
- ;;; files that others have changed. The user is shown the list of changed files (in the
- ;;; logfile) and can select 1 or many (by shift clicking) files to copy over to their disk.
- ;;; - Show all locked files. Show a list of all the locked files, along with who locked them.
- ;;;
- ;;; Hardware needed:
- ;;; Each user needs a Macintosh with access to an Appleshare network.
- ;;; You need a server machine that can be mounted from other Macs.
- ;;;
- ;;; Software needed:
- ;;; System 7.0 (or greater) & MACL 2.0 (or greater)
- ;;;
- ;;; To install:
- ;;; Simply load this file. The LockFile menu choice will install itself. If you want to
- ;;; save this file within an application and one of your users uses the server machine
- ;;; then have (init-rcs) executed when your application is started up.
- ;;;;;;
- ;;; Known misfeatures:
- ;;; I should write some code that allows one to automatically update their
- ;;; directory.
- ;;;
- ;;; Known bugs:
- ;;; I suppose it is possible for 2 people to (almost) simultaneously unlock the same file. We
- ;;; have never had it happen to us.
- ;;; I am not sure what will happen if someone on the server machine locks a file
- ;;; outside of the shared directory.
- ;;;
- ;;; Changes you have to make:
- ;;; The only changes you should need to make for your project are to the defparameters below.
- ;;; Because a person on a server machine cannot mount their own machine
- ;;; I have a bunch of special case code that allows one to use this software
- ;;; on a server machine.
-
- (in-package :ccl)
-
- ;;; ------------------------------------------------------------------------------------------------
- ;;; change the following strings for your project. Only the 1st 3 are required to be changed.
- (defparameter *server-name* "feist") ;<used only if someone is using the server machine>
- ;put fileserver name here. This is the chooser name.
- (defparameter *home-directory* "ccl;physics:") ;Local home directory where the project files are kept.
- ;This is where a file ends up when locked and copied.
- (defparameter *outsider-server-volume* "physics (shared):")
- ;Server volume where the project files are kept
- ;If someone is running on the server machine we
- ;assume this is in ccl; (see below)
- (defparameter *folder-of-outsider-server-volume-on-server* "ccl;")
- ;<used only if someone is using the server machine>
- ;location of *outsider-server-volume on server machine
- ;e.g. on server machine -- ccl;physics (shared):
- (defparameter *filename-locked-file-list-file* "locked-file-list") ;File for list of locked files
- (defparameter *filename-log-file* "logfile") ;File for documentation on changes made to files
- ;;; ------------------------------------------------------------------------------------------------
- (defvar *locked-file-list-file*) ; full pathname of locked-file-list-file
- (defvar *log-file*) ; full pathname of log file
- (defvar *server-servers-volume* nil) ;server access to server volume
- (defvar *server-volume*) ; The server machine relative to the user.
- (defvar *locked-file-list*) ; temporary list holding the contents of locked-file-list-file
- (defvar *rcs-menu*) ; lock file menu
-
- (defun on-server-p nil (equal (machine-instance) *server-name*))
-
- (defmacro concat (&rest strings)
- `(concatenate 'string ,@strings))
-
- ;;; init-rcs is called automatically at the end of this file
- (defun init-rcs nil
- (setq *home-directory* (mac-namestring *home-directory*))
- (setq *server-servers-volume* (mac-namestring
- (concat *folder-of-outsider-server-volume-on-server*
- *outsider-server-volume*)))
- (if (on-server-p)
- (setf (logical-pathname-translations
- ;; take out the colon at the end of *outsider-server-volume*
- (subseq *outsider-server-volume* 0 (1- (length *outsider-server-volume*))))
- ;; copied right out of steele without understanding it...
- `(("**;*.*.*" ,(concat *server-servers-volume* "**")))))
- (setq *server-volume* *outsider-server-volume*)
- (setq *locked-file-list-file* (concatenate 'string *server-volume* *filename-locked-file-list-file*))
- (setq *log-file* (concatenate 'string *server-volume* *filename-log-file*))
-
- (if (find-menu "LockFile") (menu-deinstall *rcs-menu*))
- (setq *rcs-menu* (make-instance 'menu :menu-title "LockFile"))
- (add-menu-items *rcs-menu*
- (make-instance 'menu-item
- :menu-item-title "Lock a project file and copy to local disk"
- :menu-item-action #'lock-project-file)
- (make-instance 'menu-item
- :menu-item-title "Unlock project file and copy new version to server"
- :menu-item-action #'unlock-project-file)
- (make-instance 'menu-item
- :menu-item-title "Unlock project file but don't copy new version to server"
- :menu-item-action #'unlock-file-dont-copy)
- (make-instance 'menu-item
- :menu-item-title "Copy newly created file to server."
- :menu-item-action #'copy-new-file-to-server)
- (make-instance 'menu-item
- :menu-item-title "Copy logged files to local disk."
- :menu-item-action #'copy-logfiles-to-local-disk)
- (make-instance 'menu-item
- :menu-item-title "Show all locked files"
- :menu-item-action #'find-all-locked-files)
- )
- (menu-install *rcs-menu*)
-
- (load-locked-file-list)
-
-
- )
-
- ;;; This is what users will call
- #|
-
- ;;; This is what programmers will call
- (defun copy-experimental-project (&optional (purge nil))
- (when (eq t (catch-cancel
- (y-or-n-dialog "Are you sure you want to copy the experimental directory to the local disk?")))
- (show-listener)
- (format t "~%Please wait as I copy the project directory to the local disk...~%")
- (copy-directory *experimental-project-directory* *home-directory* t purge)
- (format t "DONE!")
- ))
- |#
-
- (defun server-to-logical-server-name (file)
- (concat *server-volume*
- (strip-left (namestring (translate-logical-pathname *server-volume*))
- file)))
-
- ;;; lock a file on the experimental directory
- (defun lock-project-file nil
- (let (longfilename
- tofile
- tofileyounger
- (default-choose-directory (choose-file-default-directory))
- )
- (when (string-equal (machine-instance) "")
- (message-dialog "Aborted because you have not named your Mac. Please name your computer in Sharing Setup in Control Panels.")
- (return-from lock-project-file))
- (setq longfilename
- (catch-cancel
- (choose-file-dialog :directory *server-volume*
- )))
- (set-choose-file-default-directory default-choose-directory)
- (when (neq longfilename :cancel)
- (setq longfilename (namestring longfilename))
- (setq longfilename (server-to-logical-server-name longfilename))
- (when (is-locked-filep longfilename)
- (message-dialog (concat longfilename " is already locked. Aborting request..."))
- (return-from lock-project-file))
- (setq tofile (server-to-home-name longfilename))
- (setq tofileyounger (is-youngerp tofile longfilename))
- (when (or (not tofileyounger)
- (and tofileyounger
- (eq t (catch-cancel (y-or-n-dialog
- "The file on the local disk is younger than the one on the server. Should I still copy it?")))))
- (if (probe-file tofile) (unlock-file tofile))
- (copy-file longfilename tofile
- :if-exists :overwrite)
- (lock-and-print longfilename tofile)
- ))))
-
- (defun lock-and-print (serverfilename homefilename)
- (let (shortfilename)
- (setq shortfilename (file-namestring serverfilename))
- (update-locked-file-list serverfilename :add)
- (if (y-or-n-dialog
- (concat shortfilename " has been copied to your disk and is locked. To edit the file click on EDIT, otherwise click on OK.")
- :yes-text "EDIT" :no-text "OK" :cancel-text nil)
- (ed homefilename))))
-
- (defun is-youngerp (file1 file2)
- (and (probe-file file1) (probe-file file2) (> (file-write-date file1) (file-write-date file2))))
-
- ;;; format of locked-file-list is ((filename . person) (filename . person) ...)
-
- (defun is-locked-filep (filename)
- (load-locked-file-list)
- (assoc filename *locked-file-list*
- :test #'string-equal))
-
- (defun load-locked-file-list nil
- (if (null (probe-file *locked-file-list-file*))
- (with-open-file (stream *locked-file-list-file* :direction :output)
- (print nil stream)))
- (setq *locked-file-list*
- (with-open-file (stream *locked-file-list-file* :direction :input)
- (read stream))))
-
- (defun save-locked-file-list nil
- (let ((tempfilename (concat *locked-file-list-file* "temp")))
- (with-open-file (stream tempfilename :direction :output :if-exists :supersede)
- (print *locked-file-list* stream))
- (rename-file tempfilename *locked-file-list-file* :if-exists :overwrite)))
-
- (defun username nil (machine-instance))
-
- (defun make-pair (&key filename person)
- (cons filename person))
- (defun get-person (pair)
- (rest pair))
- (defun get-filename (pair)
- (first pair))
-
- ;;; ------------------------------------------------------------------------------------
- (defun unlock-project-file (&optional (dontcopyflag nil))
- (let ((username (machine-instance))
- (homefilename)
- (serverfilenames))
- (when (eql username "")
- (message-dialog "Aborted because you have not named your Mac. Please name your computer in Sharing Setup in Control Panels.")
- (return-from unlock-project-file))
- (setq serverfilenames
- (catch-cancel
- (select-item-from-list (find-my-locked-files) :selection-type :disjoint)))
- (when (neq serverfilenames :cancel)
- (dolist (serverfilename serverfilenames)
- ;; doncopyflag means unlock the file but don't copy your version to the project directory
- (when (null dontcopyflag)
- (setq homefilename (server-to-home-name serverfilename))
- (if (probe-file homefilename)
- (copy-to-server-and-update-logfile homefilename serverfilename)
- (format t "You do not have ~a to copy to the project directory~%" homefilename))
- )
- (update-locked-file-list serverfilename :delete)
- ))))
-
- ;;; Given a name on the server, construct the corresponding name on the home directory.
- (defun server-to-home-name (filename)
- (concat *home-directory*
- (strip-left *server-volume* (namestring filename))))
-
- ;;; Given a name on the home directory, construct a name for the server
- (defun home-to-server-name (filename)
- (concat *server-volume*
- (strip-left *home-directory* (namestring filename))))
-
- (defun copy-to-server-and-update-logfile (homefilename serverfilename)
- (if (or (null (probe-file serverfilename))
- (>= (file-write-date homefilename) (file-write-date serverfilename))
- (eq t (catch-cancel (y-or-n-dialog
- "The file on the local disk is older than the one on the server. Should I still copy it?"))))
- (progn
- (copy-file homefilename serverfilename :if-exists :overwrite)
- ;; make sure the dates on both files are the same in case clocks are off on
- ;; both machines.
- (set-file-write-date homefilename (file-write-date serverfilename)))
- (return-from copy-to-server-and-update-logfile))
- (update-log-file serverfilename))
-
-
- ;;; BUGS: doesn't check to see if the file already exists on the server
- (defun copy-new-file-to-server nil
- (let (homefilename serverfilename)
- (message-dialog "Please select a newly created file to copy to the server.")
- (setq homefilename
- (catch-cancel (choose-file-dialog :directory *home-directory*
- )))
- (when (neq homefilename :cancel)
- (setq homefilename (namestring homefilename))
- (setq serverfilename (home-to-server-name homefilename))
- (if (eq t (catch-cancel (y-or-n-dialog
- (concat "Can I store" homefilename " as " serverfilename "?"))))
- (copy-to-server-and-update-logfile homefilename serverfilename)
- (message-dialog "Aborting the copy ...")))))
-
- (defun update-locked-file-list (file operation)
- (let ((newpair (make-pair :filename file :person (username))))
- (cond
- ((eq operation :add)
- (pushnew newpair *locked-file-list*))
- ((eq operation :delete)
- (setq *locked-file-list*
- (delete newpair *locked-file-list* :test #'equal)))
- (t (error "illegal operation in update-locked-file-list")))
- (save-locked-file-list)))
-
- (defun update-log-file (filename)
- (setq filename (namestring filename))
- (let ((changes))
- (with-open-file (stream *log-file* :direction :output :if-exists :append :if-does-not-exist :create)
- (setq changes (catch-cancel
- (get-string-from-user (concat "File " filename " has been copied to the server. Describe your changes to the file here."))))
- (format stream "~a \"~a\" ~a -- ~a~%" (machine-instance) filename (return-the-date) changes)
- )))
-
- (defun return-the-date nil
- (multiple-value-bind (second minute hour date month year
- day-of-week daylight-saving-timep time-zone)
- (get-decoded-time)
- (declare (ignore second year day-of-week daylight-saving-timep time-zone))
- (format nil "(~a:~2,'0d ~a/~2,'0d)" hour minute month date)))
-
- (defun find-my-locked-files nil
- (find-user-locked-files (username)))
-
- (defun find-user-locked-files (user)
- (mapcar 'get-filename
- (remove user *locked-file-list*
- :test #'(lambda (user y) (not (equal user (get-person y)))))))
-
- (defun find-people-with-locked-files nil
- (let (people)
- (dolist (pair *locked-file-list*)
- (pushnew (get-person pair) people :test #'equal))
- people))
-
-
- (defun find-all-locked-files nil
- (load-locked-file-list)
- (format t "~%-------------------~%")
- (dolist (person (find-people-with-locked-files))
- (show-listener)
- (format t "Locked files for ~a:~%" person)
- (dolist (file (find-user-locked-files person))
- (format t " ~a~%" file))))
-
- (defun show-listener nil
- (window-select (find-window "Listener")))
-
- (defun unlock-file-dont-copy nil
- (unlock-project-file t))
-
- ;;; copy a file and make sure the write dates are the same on both files
- (defun copy-file-and-set-write-date (fromfile tofile)
- (copy-file fromfile tofile :if-exists :overwrite)
- (set-file-write-date tofile (file-write-date fromfile)))
-
- ;;;-----
- ;;; Copy files from logfile to local disk. Remove duplicate names in logfile list of files.
- ;;; BUGS: doesn't check to see if local files are more recent than server files.
- (defun copy-logfiles-to-local-disk nil
- (let (linelist selectlist tofile fromfilelist)
- (with-open-file (finput *log-file* :direction :input)
- (setq linelist
- (do* ((line (read-line finput nil :eof)(read-line finput nil :eof))
- (linelist)
- (pos))
- ((eq line :eof) linelist)
- (setq pos (position #\" line)) ;kludge for testing for a filename in line
- (if pos
- (push line linelist)))))
- (setq selectlist
- (catch-cancel
- (select-item-from-list linelist :selection-type :disjoint)))
- (when (and selectlist (not (eq selectlist :cancel)))
- (show-listener)
- (setq fromfilelist
- (mapcar #'(lambda (line) (read-from-string line nil nil :start (position #\" line)))
- selectlist))
- (setq fromfilelist (remove-duplicates fromfilelist :test #'string-equal))
- (dolist (fromfile fromfilelist)
- (if (probe-file fromfile)
- (progn
- (setq tofile (server-to-home-name fromfile))
- (format t "~%About to copy file ~a to ~a -- " fromfile tofile)
- (copy-file-and-set-write-date fromfile tofile)
- (format t "DONE"))
- (format t "~%Did not copy file ~a because I could not find it." fromfile))))))
-
-
- #|
- ;;; copy one directory to another directory
-
- ;;; verboseflag,if true, prints out a DOT when a file is read in
- ;;; purge, if true, deletes the destination directory
- (defun copy-directory (from to &optional (verboseflag t) (purge nil))
- (setq from (namestring from)
- to (namestring to))
- (if verboseflag (show-listener))
- (if (and (probe-file from) (directoryp from) (directoryp to) (not (equal from to)))
- (progn
- (if (or purge (null (probe-file to)))
- (create-file to :overwrite t))
- (dolist (fromfile (list-of-files from))
- (let* ((filename (file-namestring fromfile))
- (tofile (merge-pathnames to filename))
- (tofilepresent (probe-file tofile))
- (fromfilewritedate (file-write-date fromfile))
- (tofilewritedate (and tofilepresent (file-write-date tofile))))
- ;;copy only if no file or new version of file
- (if verboseflag (princ "."))
- (cond ((or (null tofilepresent)
- (< tofilewritedate fromfilewritedate))
- (if tofilepresent (unlock-file tofile))
- (copy-file fromfile tofile :if-exists :overwrite)
- (set-file-write-date tofile fromfilewritedate))
- ((and tofilewritedate (> tofilewritedate fromfilewritedate))
- (format t "~%Warning...Your version of ~a is newer than the server's version."
- filename)))))
- (do-directories-in-directory (dir from)
- (let* ((newfromdir (pathname-directory dir))
- (newpartdir (strip-left from newfromdir))
- (newtodir (concat to newpartdir)))
- (copy-directory newfromdir newtodir verboseflag purge))))
- (format t "Did not copy ~a to ~a" from to)))
- |#
-
- ;;; strip (length sub) characters from the left part of seq
- ;;; Used to strip off part of a directory from seq
- ;;; e.g. (strip-left "hd:" "hd:foo:") --> "foo:"
- (defun strip-left (sub seq)
- (subseq seq (length sub)))
-
- ;;; Return a list of files in directory "dir"
- ;;; function is probably WRONG
- (defun list-of-files (dir)
- (directory (concat dir "*.*")))
-
- ;;; ------------------------------------------------------------------------------
- (init-rcs)